#!/usr/sbin/rsct/perl5/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 1999,2002 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# "@(#)95   1.41   src/rsct/registry/cli/bin/lssrtbldef.perl, srcli, rsct_rpyxh, rpyxht1f3 2/22/01 16:23:31"
######################################################################
#                                                                    #
# Module: lssrtbldef                                                 #
#                                                                    #
# Purpose:                                                           #
#   lssrtbldef - Gets the table definition/meta data.                #
#                                                                    #
# Syntax:                                                            #
#   lssrtbldef [-h][-MINcr][-Csntqv][-l|i|d|D Delimiter][-x] [-TV]   #
#                           table [column_name ...]                  #
#   -OR-                                                             #
#   lssrtbldef [-h][-TV] -e table [sd_column_name ...]               #
#                                                                    #
# Flags:                                                             #
#   -h  help - writes this command's usage statement to stdout.      #
#   -M  Table Metadata. Displays the fixed portion of the metadata - #
#       table name, number of rows, number of columns, table         #
#       properties, table change counter.                            #
#   -I  Display table change counter.                                #
#   -N  Name. Displays the name of the table.                        #
#   -c  Column count. Displays the number of columns in the table.   #
#   -r  Row count. Displays the number of rows in the table.         #
#   -C  Column metadata, display the column definitions from the     #
#       table metadata for all columns in the table or for the       #
#       columns specified by the column_name operand - column name,  #
#       type, qualifier, default value, row change counter.          #
#   -s  Print System (SY)-qualified columns for all columns in the   #
#        table or for the columns specified by the Column operand.   #
#   -n  Column name. Displays names of all columns in the table or   #
#       for the columns specified by the Column operand.             #
#   -t  Column Type. Displays the types of all columns in the table  #
#        or for the columns specified by the Column operand.         #
#   -q  Column Qualifier. Displays the qualifiers of all columns in  #
#       the table or for the columns specified by the Column operand.#
#   -v  Column Default Value. Displays the default values for all    #
#       columns in the table or for the columns specified by the     #
#       column_name operand.                                         #
#   -l  Long form output. Metadata descriptions included in output.  #
#   -i  Input file format. Display output in a format suitable for   #
#       use as input to mksrrow.                                     #
#   -d  Delimiter formatted output. The default delimiter is a colon #
#       ":", use the -D flag if you wish to change the default       #
#       delimiter.                                                   #
#   -D  Delimiter formatted output using a specified delimiter. Use  #
#       this flag to specify something other than the default colon  #
#       ":", when the data to be displayed may contain colons. Use   #
#       this flag to specify a one or more character delimiter.      #
#   -x  Exclude header. Suppress header printing.                    #
#   -T  Trace. Writes this command's trace messages to stderr.       #
#   -V  Verbose. Writes this command's verbose messages to stderr.   #
#   -e  Expand. Print out SD definition of the given column_name,    #
#       or print SD definitions for all columns in the table if no   #
#       column_names are given.                                      #
#                                                                    #
# Operands:                                                          #
#   table         The name of the table whose definition you wish to #
#                 display.  A relative or absolute table name can be #
#                 specified.                                         #
#   column_name   The name(s) of a specific column(s) whose          #
#                 definition you wish to display.                    #
#                                                                    #
# Description:                                                       #
#   The lssrtbldef command displays the table definition as it is    #
#   defined in the System Registry. The table definition may also be #
#   referred to as the table schema or table metadata. By default, if#
#   no flags are provided on the command line, the entire table      #
#   definition is displayed, including the column definitions. If    #
#   column_name operands are provided, then only column definition   #
#   data for the specified columns is displayed.                     #
#                                                                    #
#   Flags for this command are grouped by the effect they have on the#
#   output. The first group [ -TINcr ]  displays information from the#
#   fixed portion of the table metadata. Fixed data is information   #
#   that applies to the table as a whole, not to individual columns. #
#   This includes the table name, table change counter, number of    #
#   columns, and the number of rows. Use the [ -T ] flag to display  #
#   all the fixed table metadata. Otherwise, use any combination of  #
#   the other flags to display all or part of the fixed part of the  #
#   table metadata.                                                  #
#                                                                    #
#   The second group of flags [ -Csntqv ] displays information from  #
#   the variable portion of the table metadata. Variable data is     #
#   considered to be the column definitions. This includes column    #
#   name, data type, qualifier, default value and row change         #
#   counter. Use the [ -C ] flag to display all the column metadata. #
#   Otherwise, use any combination of the other flags to display all #
#   or part of the column metadata. By default only user defined     #
#   columns are displayed. Use the [ -s ] flag to display system-    #
#   defined (qualified) columns as well.                             #
#                                                                    #
#   The third group of flags [-l|i|d|D Delimiter] and [-x] modify the#
#   manner in which the data is displayed. By default, table metadata#
#   is displayed using columns and headers. Fixed table metadata is  #
#   displayed above and in a separate table from the column metadata.#
#   The [-f] flag displays only the variable portion of the data,    #
#   (the column definitions), in a format suitable to be passed into #
#   mksrtbl or mksrcol.                                              #
#                                                                    #
#   The -e flag (Expand SD definition) is held by itself because     #
#   SD definitions essentially represent tables within tables.       #
#   Each SD definition is printing out under it's own heading, using #
#   the long print format.                                           #
#                                                                    #
#   Flag compatibility is as follows:                                #
#   [-T] overrides [-INcr] because it includes all the other flags.  #
#   [-C] overrides [-ntqv] because it includes all the other flags.  #
#   [-x] and [-l|i] flags can not be used simultaneously.  If        #
#   they are used, the [-x] flag is ignored. (Long form or file      #
#   format displays always use headers.)                             #
#   -e is used by itself. All other flag options are ignored.        #
#   All other flag combinations are allowed.                         #
#                                                                    #
# Exit Values:                                                       #
#   0  SR_CLI_SUCCESS        Command completed successfully.         #
#   1  SR_CLI_REGISTRY_ERROR Command terminated due to an underlying #
#                            System Registry error.                  #
#   2  SR_CLI_ERROR          Command terminated due to an underlying #
#                            error in the command script.            #
#   3  SR_CLI_BAD_OPERAND    Command terminated due to user          #
#                            specifying a bad operand.               #
#   4  SR_CLI_BAD_FLAG       Command terminated due to user          #
#                            specifying an invalid flag.             #
#   5  SR_CLI_USER_ERROR     Command terminated due to a user error. #
#                            For example specifying an undefined     #
#                            table to be listed.                     #
#                                                                    #
# Examples:                                                          #
#   The following examples all display in tabular form               #
#   1. lssrtbldef -M /IBM/Node/Nodes -OR-                            #
#      lssrtbldef -INcr /IBM/Node/Nodes                              #
#     - prints all the table metadata (no column metadata).          #
#                                                                    #
#   2. lssrtbldef Cluster                                            #
#     - prints all available metadata on the Cluster table in        #
#     the current working directory (use pwdsr to check this.)       #
#                                                                    #
#   3. lssrtbldef -tnqvs Cluster  -OR- lssrtbldef -Cs Cluster        #
#     - prints the column name, type, qualifier default value for    #
#     all the columns of the Cluster table, including system-        #
#     qualified columns.                                             #
#                                                                    #
#   Examples with other formats:                                     #
#   4. lssrtbldef -nqvD ::  /IBM/Node/Nodes                          #
#     - print only the name, qualifier and default value of the      #
#     columns of the table /IBM/Node/Nodes, delimitted by '::'.      #
#                                                                    #
#   5. lssrtbldef -INl /samples/Tudors                               #
#     - print only the table change counter and name of the given    #
#     table, but using long form output.                             #
#                                                                    #
#   6. lssrtbldef -e /samples/Tudors                                 #
#     - print all the SD definitions for columns in /samples/Tudors  #
#     that are either structured data or structured data arrays.     #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /usr/sbin/rsct/msgmaps/srcli.lssrtbldef.map - message mapping    #
#                                                                    #
# Outputs:                                                           #
#   stdout - display of the table definition / metadata and help     #
#   stderr - display of verbose, trace and error messages            #
#                                                                    #
# External Ref:                                                      #
#   Commands: $LSMSG                                                 #
#   Extensions:  SR.pm, CT.pm, SRrc.pm                               #
#   Modules:  SR_cli_utils.pm                                        #
#   Perl library routines: Getopts::Std                              #
#                                                                    #
# Tab Settings:                                                      #
#   4 and tabs should be expanded to spaces before saving this file. #
#   in vi:  (:set ts=4  and   :%!expand -4)                          #
#                                                                    #
# Change Activity:                                                   #
#   000929 HGJ 38317: Initial delivery.                              #
#                                                                    #
######################################################################

#--------------------------------------------------------------------#
# General Program Flow/Logic:                                        #
#                                                                    #
# A. Parse command line flags and parameters.                        #
# B. Initialize a session with the System Registry.                  #
# C. Open the table, get the table metadata, nicely display which    #
#    pieces of the table metadata the user asked for.                #
# D. Free the storage associated with the table metadata and         #
#    close the table.                                                #
# E. Terminate the Session with the system registry and any other    #
#    necessary cleanup.                                              #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# Included Libraries and Extensions                                  #
#--------------------------------------------------------------------#
use lib "/usr/sbin/rsct/pm";
use locale;
use Getopt::Std; 

use Env qw(COLUMNS);

use CT::CT qw(CT_SD_PTR CT_SD_PTR_ARRAY);
use CT_cli_utils qw(
    printIMsg
    printEMsg
);
use CT_cli_data_type_utils qw(
    data_type_to_string
);
use CT_cli_display_utils qw(
    set_display
    convert_value
    CT_CLI_MAX_COL_WIDTH
);

use CT::SRrc;
use CT::SR;
use CT::SR qw(
    :sr_entry_t
    :sr_qualifier_t
);
use SR_cli_rc qw(:return_codes);
use SR_cli_utils qw(
    init_session
    term_session
    isRelative
    printCEMsg
    printCIMsg
    $DEFAULT_GLOBAL_MOUNT_POINT
    free_table_metadata
    open_table
    set_session_variables
    clean_session
    error_exit
); 


#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
# Constants
$TRUE       = 1;
$FALSE      = 0;
$SD_HEADER  = "_SDDefinition";
$SR_CLI_MAX_COL_WIDTH = CT_CLI_MAX_COL_WIDTH;

$Verbose    = $FALSE;                  # default - verbose turned off
$Trace      = $FALSE;                  # default - trace turned off

# Input flag variables
# Fixed data
$Opt_Name               = $FALSE;      # default - see -N   
$Opt_Columns            = $FALSE;      # default - see -c
$Opt_Rows               = $FALSE;      # default - see -r 
$Opt_Tbl_Ctr            = $FALSE;      # default - see -i

# Variable data
$Opt_Column_Name        = $FALSE;      # default - see -n
$Opt_Column_Type        = $FALSE;      # default - see -t
$Opt_Column_Qualifier   = $FALSE;      # default - see -q
$Opt_Column_DefValue    = $FALSE;      # default - see -v
$Opt_Column_SDDefn      = $FALSE;      # default - see -i
$Opt_SD_Display         = $FALSE;      # default = see -e 

# Print options
$Opt_No_Header          = $FALSE;      # default - see -x
$Opt_Delimiter          = $FALSE;      # default - see -D & -d
$Opt_Print_SY           = $FALSE;      # default - see -s
$Opt_Long_Output        = $FALSE;      # default - see -l
$Opt_File_Format        = $FALSE;      # default - see -f

# Messaging variables
$PROGNAME ="lssrtbldef";               # Program Name for messages
$MSGCAT ="srcli.cat";                  # msg catalogue for this cmd
$CTDIR ="/usr/sbin/rsct";              # Cluster directory path
$CTBINDIR ="$CTDIR/bin";               # Cluster Bin directory path
$LSMSG="$CTBINDIR/ctdspmsg";           # display message rtn.
$ENV{'MSGMAPPATH'}="$CTDIR/msgmaps";   # Msg maps used by $LSMSG

%Cleanup = ();                         # Hash of items to cleanup
                                       # {Session} $session to term

#--------------------------------------------------------------------#
# Variables                                                          #
#--------------------------------------------------------------------#
# Initialise extension variables to be used
my $Table_handle = CT::SR::table_handle_t->new;
my $Table_definition = CT::SR::table_metadata_t->new;
my $Tree_handle = "";                  # initialised in init_session

# Other variables
my $rc                     = 0;        # assume a good return code
my $Table_name             = "";
my $SR_table_name          = "";
my $Set_work_dir           = $FALSE;   
my $Get_fixed_data         = $FALSE;
my $Get_variable_data      = $FALSE;

my $Mount_point = $DEFAULT_GLOBAL_MOUNT_POINT;  # Mount point


#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#
# Parse the command line, exit if there are errors 
($rc, $Table_name, $Get_variable_data, $Get_fixed_data, $Col_names) = 
    parse_cmd_line();
($rc == 0) ||  error_exit($rc); 

# If the table name is an absolute table name we must prefix it with
# the mount point.
($Set_work_dir, $SR_table_name) = set_session_variables($Table_name);

# Establish a session
# CT::SR::open_tree, CT::SR::mount_directory, 
# CT::SR::change_current_directory
# Uses Env qw(CT_SR_HOME, CT_SR_NUM_RETRIES, CT_SR_TIMEOUT) if they 
# are set.
# Assuming isRelative returns true for an empty direcoty this way
# for srls, when no directory is specified, we want to display the
# current directory or CT_SR_HOME.  So we need 
# change_current_directory to be called for CT_SR_HOME.
($rc, $Tree_handle) = init_session($Set_work_dir);
($rc == 0) ||  error_exit($rc); 

# Add the Tree handle to the cleanup hash
$Cleanup{Session} = $Tree_handle;


# TODO: Trace messages in this file are temporary and will
# not be catalogued.  Tracing support will be added when the 
# C API is available and Perl to C extensions have been built.
# Feature 48401

($rc, $Table_handle) = open_table($Tree_handle, $SR_table_name, 
                        $Table_name, SR_READ);
($rc == 0) || error_exit($rc); 

# Add the table handle to the cleanup hash
push @{$Cleanup{Tables}}, $Table_handle;


# Now get the table metadata
$Verbose && printIMsg("IMsglssrtbldefListingDef", $Table_name);

# Metadata is gathered based on whether variable portions of
# the data are required or not. (Based on flag input)

# Print variable portions of the data if requested

if ($Get_variable_data || $Opt_SD_Display) { 
    $Trace && print STDERR "Calling CT::SR::get_table_metadata(all_metadata) \n";
    $rc = CT::SR::get_table_metadata($Table_handle, 1, 
                        $Table_definition);
}
else {
    $Trace && print STDERR "Calling CT::SR::get_table_metadata(fixed data)\n";
    $rc = CT::SR::get_table_metadata($Table_handle, 0, 
                        $Table_definition);
}
$Trace && print STDERR "CT::SR::get_table_metadata return code $rc\n";

$rc = error_check("sr_get_table_metadata", $rc, $Table_name);
($rc == 0) || error_exit($rc);


$Column_definitions = $Table_definition->getColumnDefs();

if ($Opt_SD_Display) {

    # This signals printing out SDs, which are always file
    # format for now (05/08/00)

    $Trace && print STDERR "Entering create_sd_display\n";
    $rc = create_sd_display($Column_definitions, $Col_names, 
                        $Opt_No_Header);
    $Trace && print STDERR "Exited create_sd_display\n";

}
elsif ($Get_variable_data) {

    # Print column definitions based on flag requests

    if ($Opt_Long_Output) { 
        $rc = create_display($Table_name, $Table_definition, 
            $Column_definitions, "long", $Opt_No_Header, 
            $Opt_Print_SY, $Opt_Delimiter, $Col_names); 
    }
    elsif ($Opt_File_Format) {
        $rc = create_display($Table_name, $Table_definition, 
            $Column_definitions, "file", $Opt_No_Header, 
            $Opt_Print_SY, $Opt_Delimiter, $Col_names); 

        print "\n";
        $rc = create_sd_display($Column_definitions, $Col_names, 
            $Opt_No_Header);
    }
    elsif ($Opt_Delimiter) {
        $rc = create_display($Table_name, $Table_definition, 
            $Column_definitions, "delim", $Opt_No_Header, 
            $Opt_Print_SY, $Opt_Delimiter, $Col_names); 
    }
    else {  # Print column output by default
        $rc = create_display($Table_name, $Table_definition, 
            $Column_definitions, "column", $Opt_No_Header, 
            $Opt_Print_SY, $Opt_Delimiter, $Col_names); 
    }

} # end if ($Get_variable_data)
elsif ($Get_fixed_data) {
    # Print any fixed portions of the data if requested
    # file (-i) format output does not print table metadata 
    # (fixed data)
    if ($Opt_Long_Output) { 
        print_fixed_data("long", $Opt_No_Header, 
            $Opt_Delimiter, $Table_definition); 
    }
    elsif ($Opt_Delimiter) { 
        print_fixed_data("delim", $Opt_No_Header, 
            $Opt_Delimiter, $Table_definition); 
    }
    else {
        print_fixed_data("column", $Opt_No_Header, 
            $Opt_Delimiter, $Table_definition); 
    }               

}

# Exit with the least non-zero error code
($rc == 0) || error_exit($rc);

# Free table metadata
$rc = free_table_metadata($Table_definition);
($rc == 0) || error_exit($rc);


# Try to gracefully close the session and quit with worst rc
$rc = clean_session($Tree_handle, $Mount_point, $Table_handle);

# If the code gets to here then $rc could still be non-zero
%Cleanup = ();
($rc == 0) || error_exit($rc);

# $rc is non-zero here
exit $rc;

#--------------------------------------------------------------------#
# End Main Code                                                      #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# create_sd_display:                                                 #
#   Given table metadata, if no column list given, then get a list   #
#   of all the CT_SD_PTR and CT_SD_PTR_ARRAY columns to print, then  #
#   call print_sd_definitions.                                       #
#                                                                    #
# Parameters:                                                        #
#   $metadata     - table metadata to use                            #
#   $sds_to_print - array of columns to print (could be empty)       #
#   $no_header    - (boolean) header suppression flag                #
#  NOTE: These parameters left documeted, if not used because in     #
#  future there might be other than file format used for these       #
#   $system_columns - (boolean) print system qualified columns       #
#   $delim        - delimiter to use, if given                       #
#                                                                    #
# Returns:                                                           #
#   $local_rc     - 0 if command completes successfully              #
#--------------------------------------------------------------------#
sub create_sd_display 
{
my ($column_defs, $sds_to_print, $no_header) = @_;
my $local_rc = 0;

# Set up the output array as follows:
# Row headers are 'element <number>:'
# Column headers are name, type, default_value
# Data is the data from the sd_defn portion of the hash.

my @sd_to_print = ();

$Trace && print STDERR "Entered create_sd_display\n";

if (scalar (@$sds_to_print) == 0 ) {
    # No column names given, so
    # make an array of all the SD_PTR and SD_PTR_ARRAY columns.

    foreach $hash_rv (@$column_defs) {

        if (($hash_rv->{type} == CT_SD_PTR) || 
                ($hash_rv->{type}== CT_SD_PTR_ARRAY)) {
            push @sd_to_print, $hash_rv->{name};    
        }                   

    } 

    $local_rc = print_sd_definitions(\@sd_to_print, $column_defs);
}
else { 
    $local_rc = print_sd_definitions($sds_to_print, $column_defs);
}

$Trace && print STDERR "Leaving create_sd_display\n";

return($local_rc);
}   # end create_sd_display


#--------------------------------------------------------------------#
# print_sd_definitions:                                              #
#   Given a list of columns containing SD definitions, the break-    #
#   down of each SD is printed, including the name, data type and    #
#   default value of each element in the SD.                         #
#   By default this is done using input file format (-f).            #
#   All other formats are overridden at this time.                   #
#                                                                    #
# Parameters:                                                        #
#   $sds_to_print - column names to be printed                       #
#   $col_defs     - column definition data from $metadata->getColDefs#
#   $no_header    - (boolean) header suppression flag                #
#   $system_columns - (boolean) print system qualified columns       #
#   $delim        - delimiter to use, if given                       #
#                                                                    #
# Returns:                                                           #
#   $local_rc     - 0 if command completes successfully              #
#                                                                    #
#--------------------------------------------------------------------#
sub print_sd_definitions 
{
my ($sds_to_print, $col_defs) = @_;

my @columns = ();
my $sd_name = "";
my $element_ind = 0;
my ($temp_default, $value, $sd_names, $sd_types, $hash_rv, $row, $col);
my $local_rc = 0;

foreach $sd_name (@$sds_to_print) { 
    @columns = ();

    # Add column headers
    $columns[0][1] = "name";
    $columns[0][2] = "type";
    $columns[0][3] = "def";

    # If this happens, then take the values as they
    # are in column_names and put them to the array

    my $found = 0;
    foreach $hash_rv (@$col_defs) {
        # Convert the entry and put it in an array cell

# Hanging on to this just in case for some bizarre reason SDs could
# be system columns.
#            if (($hash_rv->{"type"} == SR_SYSTEM) && 
#                        (!$system_columns)) {next;}

        if ($sd_name ne $hash_rv->{"name"}) { next; }
        else {$found = 1;}

        # Convert the entry and put it in an array cell
        $row = 1;
        $col = 0;


        if (($hash_rv->{type} == CT_SD_PTR) || 
                ($hash_rv->{type}== CT_SD_PTR_ARRAY)) {
            $columns[0][0] = $hash_rv->{name}.$SD_HEADER;

            # Should only be two arrays in this - name and type
            $sd_names = $hash_rv->{sd_defn}->[0];
            $sd_types = $hash_rv->{sd_defn}->[1];

            $row = 1;
            $element_ind = 0;

            # Name and type array assumed to have the same size
            $count = scalar(@$sd_names);

            while ($row <= $count) {
                # copy name over
                $columns[$row][1] = $sd_names->[$row-1];
                # copy type over
                $columns[$row][2] = data_type_to_string($sd_types->[$row-1]);
                $row++;
            }

            # Get the default value and set up the element number
            # for completing the display
            $row = 1;
            if ($hash_rv->{type} == CT_SD_PTR_ARRAY) {
                $temp_default = $hash_rv->{default}->[0];
            }
            else {
                $temp_default = $hash_rv->{default};
            }
            foreach $value (@$temp_default) {
                $columns[$row][0] = "element $element_ind:";
                $columns[$row++][3] = convert_value($value->{type}, 
                                $value->{value});
                $element_ind++;
            }

            # Display this SD using file format so that the SD name
            # is printed.
            # In the arg list:
            # 4 is the set number of columns. 
            # 0 is the no_header option. 

            # TODO: This might be expanded to print other than just
            # file format in future
            set_display("file", 0, $row-1, 4, \@columns, ":");
            print "\n";
        }

    } # end foreach $hash_rv

   if (!$found) {
       printCEMsg("EMsgSRcliInvalidColumnName", $temp);
       return SR_CLI_USER_ERROR;
    }
        
} 

return ($local_rc);
}   # end create_sd_display


#--------------------------------------------------------------------#
# create_display:                                                    #
#   Write table data to STDOUT in column, long, file, or delimited   #
#   format. Calls either column_display, long_display, or            #
#   delim_display from SR_display utils.                             #
#                                                                    #
# Parameters:                                                        #
#   $table_name   - name of the table to be printed                  #
#   $metadata     - table metadata to be printed                     #
#   $format       - either column, long or delim                     #
#   $no_header    - (boolean) header suppression flag                #
#   $system_columns - (boolean) print system qualified columns       #
#   $delim        - delimiter to use, if given                       #
#   $column_names - list of column names (optional) to be displayed  #
#                                                                    #
# Returns:                                                           #
#   $local_rc     - 0 if command completes successfully              #
#                                                                    #
# Globals Altered:                                                   #
#   None                                                             #
#--------------------------------------------------------------------#
sub create_display 
{
# Grab input parameters
my $table_name = shift;
my $metadata = shift;
my $column_defs = shift;
my $format = shift;
my $no_header = shift;
my $system_columns = shift;
my $delim = shift;
my $column_names = shift;


# Set up variables needed
my @columns = ();
my $row_index = 0;
my $col_index = 0;
my $local_rc = 0;
my $binary_found = 0;
my $col_count = $metadata->getNumColumns();
my @col_types = ();
my $index = 0;

$Trace && print STDERR "Entered create_display(\"$format\")\n";

# Display values for the column qualifiers
%qual_hash = (
        0 => "Unknown qualifier",
        1 => "st",
        2 => "sy",
        3 => "pk",
);

# Initialise variables
my $spacing_size = length("\tdef = ");
my $col = 0;
my $row = 1;
my $ind = 0;
my $data_type;

if (scalar (@$column_names) == 0 ) {
    # If this happens, then just take all the data from the
    # hashes and put it into the array
    
    foreach $hash_rv (@$column_defs) {
        # Convert the entry and put it in an array cell
        if ( ($hash_rv->{qualifier} == SR_SYSTEM) && !$system_columns )
            { next; }

        $columns[$row][$col++] = "column $ind:";

        # Column Name is always displayed
        $columns[$row][$col++] = $hash_rv->{"name"};

        if ($Opt_Column_Type) { # get column types
            $data_type =    
                        data_type_to_string($hash_rv->{"type"}); 
            $columns[$row][$col++] = 
                        data_type_to_string($hash_rv->{"type"}); 
        }
        if ($Opt_Column_Qualifier) { # get column qualifiers
            $columns[$row][$col++] = 
                        $qual_hash{$hash_rv->{"qualifier"}}; 
        }
        if ($Opt_Column_Prop) { # get column properties
            $columns[$row][$col++] =  $hash_rv->{"properties"};
        }
        if ( $Opt_Column_SDDefn) {
            if ( ($hash_rv->{type} == CT_SD_PTR) || 
                ($hash_rv->{type} == CT_SD_PTR_ARRAY) ) {
            $columns[$row][$col++] = $hash_rv->{name}.$SD_HEADER;
            }
            else { $col++; }
        }
        if ($Opt_Column_DefValue) { # get column default values
            $columns[$row][$col++] = 
                        convert_value($hash_rv->{"type"},$hash_rv->{"default"});
        }
            
        
        $row ++;
        $ind ++;
        $col = 0;

    } # end foreach $hash_rv
}
else { 
    foreach( @$column_names) { 
        # if this happens, then take the values as they
        # are in column_names and put them to the array

        $temp = $_;
        my $found = 0;

        foreach $hash_rv (@$column_defs) {
            # Convert the entry and put it in an array cell


            if ($temp ne $hash_rv->{name}) { next; }
            else {
                if (($hash_rv->{qualifier} == SR_SYSTEM) && 
                        (!$system_columns)) {
                    next;
                }
                else { $found = 1; }
            }

            # Convert the entry and put it in an array cell
            $columns[$row][$col++] = "column $ind:";

            # Column Name is always displayed
            $columns[$row][$col++] = $hash_rv->{name};

            if ($Opt_Column_Type) { # get column types
                $columns[$row][$col++] = 
                        data_type_to_string($hash_rv->{type}); 
            }
            if ($Opt_Column_Qualifier) { # get column qualifiers
                $columns[$row][$col++] = 
                        $qual_hash{$hash_rv->{qualifier}}; 
            }
            if ($Opt_Column_Prop) { # get column properties
                $columns[$row][$col++] =  $hash_rv->{properties};
            }
            if ( $Opt_Column_SDDefn) {
                if ( ($hash_rv->{type} == CT_SD_PTR) || 
                    ($hash_rv->{type} == CT_SD_PTR_ARRAY) ) {
                $columns[$row][$col++] = $hash_rv->{name}.$SD_HEADER;
                }
                else { $col++; }
            }
            if ($Opt_Column_DefValue) { # get column default values
                $columns[$row][$col++] = 
                        convert_value($hash_rv->{"type"}, $hash_rv->{"default"});
            }

            $row ++;
            $ind ++;
            $col = 0;

        } # end foreach $hash_rv

        if (!$found) {
            printCEMsg("EMsgSRcliInvalidColumnName", 
                                    $temp);
            return SR_CLI_USER_ERROR;
        }
    } 

}

# Assumption: at this point I have the first column of the
# output array populated with the column names to be
# displayed. Now I have to go through that list and
# pull out the appropriate data from the column definitions.
    
# Set up the first row as the headers
$col = 1;
if ($format eq "file") {

#   if ($Opt_Column_Name) {
        $columns[0][$col++] = "col";
        $col_count++;
#    }
    if ($Opt_Column_Type) { 
        $columns[0][$col++] = "type"; 
        $col_count++;
    }
    if ($Opt_Column_Qualifier) { 
        $columns[0][$col++] = "qual"; 
        $col_count++;
    }
    if ($Opt_Column_Prop) { 
        $columns[0][$col++] = "prop"; 
        $col_count++;
    }
    if ($Opt_Column_SDDefn) { 
        $columns[0][$col++] = "sd_defn"; 
        $col_count++;
    }
    if ($Opt_Column_DefValue) { 
        $columns[0][$col++] = "def"; 
        $col_count++;
    }
}
else {
    # The strings used here are directly from the sr_column_t 
    # structure in ct_sr_types.h. (So don't have to be
    # translated for NLS requirements.)

# At this time, the name of a column is always displayed.
# Perhaps this isn't the correct way? 
# when the -N flag is implemented, then this if
# statement can be put back in. This and the previous one.
#    if ($Opt_Column_Name) {
        $columns[0][$col++] = "name";
        $col_count++;
#    }
    if ($Opt_Column_Type) { 
        $columns[0][$col++] = "type"; 
        $col_count++;
    }
    if ($Opt_Column_Qualifier) { 
        $columns[0][$col++] = "qualifier"; 
        $col_count++;
    }
    if ($Opt_Column_Prop) { 
        $columns[0][$col++] = "properties"; 
        $col_count++;
    }
    if ($Opt_Column_SDDefn) { 
        $columns[0][$col++] = "sd_defn"; 
        $col_count++;
    }
    if ($Opt_Column_DefValue) { 
        $columns[0][$col++] = "default"; 
        $col_count++;
    }
}

# Print any fixed portions of the data if requested
# file (-i) format output does not print table metadata (fixed data)
if (($format ne "file") and $Get_fixed_data) { 
        $Trace && 
            print STDERR "Entering print_fixed_data(\"$format\")\n";
        print_fixed_data($format, $Opt_No_Header, 
             $Opt_Delimiter, $metadata); 
        print "\n";             # separate fixed data from column data
        $Trace && print STDERR "Exited print_fixed_data\n";

}

# This header is used for file format display
$columns[0][0] = "TableDefinition";

set_display($format, $no_header, $row-1, $col,
        \@columns, $delim, $table_name);
  
$Trace && print STDERR "Entered create_display($local_rc)\n";

return $local_rc;
}   # end create_display


#--------------------------------------------------------------------#
# print_fixed_data -                                                 #
#   Call has included only fixed data for the table. Print result    #
#  and exit.                                                         #
#                                                                    #
# Inputs:                                                            #
#   $type       string indicating type of output: col, long, file,   #
#                or delim                                            #
#   $no_header  flag indicating header to be printed or not          #
#   $delimiter  delimiter to be used in output                       #
#   $metadata   table metadata from CT::SR::get_table_metadata call  #
#                                                                    #
# Return:                                                            #
#   $rc            0      print completed                            #
#                                                                    #
# Global Variables Referenced:                                       #
#   $SR_CLI_MAX_COL_WIDTH   input  Maximum displayable column width. #
#--------------------------------------------------------------------#
sub print_fixed_data 
{
my ($type, $no_header, $delimiter, $metadata) = @_;

# Set up local variables
my $header_string = "";
my $output_string = "";

# Assign this locally because otherwise it's too much overhead
# to access multiple times in the loops
my $max_column_width = $SR_CLI_MAX_COL_WIDTH;
my $temp = "";
my $col_length = 0;
my $name_temp = "";
my $name_length = 0;

$Trace && print STDERR "Entered print_fixed_data\n";

if ($type eq "column") {
    if (!$no_header) {              
        # These names are directly from the table_metadata_t structure
        # in ct_sr_types.h so don't have to be translatable for
        # NLS requirements. 

        if ($Opt_Name) { 
            $name_temp = $metadata->getName();
            $name_length = length($name_temp);
            if ($name_length > $max_column_width) {
                $name_length = $max_column_width;
            }
            printf "%-${name_length}s ", "name";  
        }
        if ($Opt_Columns) {print "number_of_columns ";}
        if ($Opt_Rows) {print "number_of_rows ";}
        if ($Opt_Tbl_Ctr) {print "table_change_counter ";}
# This is left in for the time that properties become relevant
#        if ($Opt_Tbl_Prop) {print "properties";}
        print "\n";
    }

    # Print data using column format
    
    if ($Opt_Name) { 
        if ($no_header) {
            $name_temp =  $metadata->getName(); 
            $name_length = length($name_temp);
        }
        if ($name_length >= $max_column_width ) {
            # Truncate the value to be printed
            $temp = substr($name_temp, 0, $max_column_width); 
        }
        printf "%-${name_length}s ", $name_temp 
    }
    if ($Opt_Columns) { 
        $temp =  $metadata->getNumColumns(); 
        if ($no_header) {
            $col_length = length($temp);
        }
        else { $col_length = 17; }

        if ($col_length >= $max_column_width ) {
            # Truncate the value to be printed
            $temp = substr($temp, 0, $max_column_width); 
        }
        printf "%-${col_length}d ", $temp 
    }
    if ($Opt_Rows) {
        $temp =  $metadata->getNumRows(); 
        if ($no_header) {
            $col_length = length($temp);
        }
        else { $col_length = 14; }

        if ($col_length >= $max_column_width ) {
            # Truncate the value to be printed
            $temp = substr($temp, 0, $max_column_width); 
        }
        printf "%-${col_length}s ", $temp 
        #printf "%-14d ", $metadata->getNumRows(); 
    }
    if ($Opt_Tbl_Ctr) {
        $temp =  $metadata->getTblChgCtr(); 
        if ($no_header) {
            $col_length = length($temp);
        }
        else { $col_length = 20; }

        if ($col_length >= $max_column_width ) {
            # Truncate the value to be printed
            $temp = substr($temp, 0, $max_column_width); 
        }
        printf "%-${col_length}s ", $temp 
        #printf "%-20d ", $metadata->getTblChgCtr(); 
    }
#    if ($Opt_Tbl_Prop) {printf "%-10d", $metadata->getProperties(); }
    print "\n";
    
}
elsif ($type eq "long") {
    if (!$no_header) {
        # Constants printed here are directly from sr_table_metadata_t
        # in ct_sr_types.h so do not need to be translated to meet
        # NLS requirements.

        if ($Opt_Name) {
            printf "name                 = %s\n", $metadata->getName();
        }
        if ($Opt_Columns) {
            printf "number_of_columns    = %d\n", $metadata->getNumColumns();
        }
        if ($Opt_Rows) {
            printf "number_of_rows       = %d\n", $metadata->getNumRows();
        }
        if ($Opt_Tbl_Ctr) {
            printf "table_change_counter = %d\n", $metadata->getTblChgCtr();
        }
#        if ($Opt_Tbl_Prop) {
#            printf "properties           = %d\n", $metadata->getProperties();
#        }

    } else {
        if ($Opt_Name) {printf "%s\n", $metadata->getName(); }
        if ($Opt_Columns) {printf "%d\n", $metadata->getNumColumns();}
        if ($Opt_Rows) {printf "%d\n", $metadata->getNumRows();}
        if ($Opt_Tbl_Ctr) {printf "%d\n", $metadata->getTblChgCtr();}
#        if ($Opt_Tbl_Prop) {printf "%d\n", $metadata->getProperties();}
    }

} # end elsif ($type eq "long") 

elsif ($type eq "delim") {
    if (!$no_header) { 
        # Constants printed here are directly from sr_table_metadata_t
        # in ct_sr_types.h so do not need to be translated to meet
        # NLS requirements.

        if ($Opt_Name) {printf "name%s", $delimiter; }
        if ($Opt_Columns) {printf "number_of_columns%s", $delimiter;}
        if ($Opt_Rows) {printf "number_of_rows%s", $delimiter;}
        if ($Opt_Tbl_Ctr) {printf "table_change_counter%s", $delimiter;}
#        if ($Opt_Tbl_Prop) {printf "properties%s", $delimiter;}
        print "\n\n";
        
    }

    # print out body of table data
    if ($Opt_Name) {
        printf "%s%s", $metadata->getName(), $delimiter; 
    }
    if ($Opt_Columns) {
        printf "%d%s", $metadata->getNumColumns(), $delimiter;
    }
    if ($Opt_Rows) {
        printf "%d%s", $metadata->getNumRows(), $delimiter;
    }
    if ($Opt_Tbl_Ctr) {
        printf "%d%s", $metadata->getTblChgCtr(), $delimiter;
    }
#    if ($Opt_Tbl_Prop) {
#        printf "%d%s", $metadata->getProperties(), $delimiter;
#    }
    print "\n\n";
}

$Trace && print STDERR "Leaving print_fixed_data(0)\n";
return 0;

}   # end print_fixed_data


#--------------------------------------------------------------------#
# parse_cmd_line - Parse the command line for options and operands.  #
#   Set appropriate global variables as outlined below, make sure we #
#   have a valid combination of arguments / options.                 #
#                                                                    #
# Return:                                                            #
#   $local_rc      0      Command line parsed fine, no problem.      #
#                  1-5    Problems parsing command line, no sense    #
#                         continuing.                                #
#   $table_name    Name of the table whose definition we want to get #
#   $column_name   Name of a specific column whose definition we     #
#                  want to get (optional).                           #
#                                                                    #
# Global Variables Modified:                                         #
#   $Opt_Name          output   (-N) display table name              #
#   $Opt_Columns       output   (-c) display # of columns in table   #
#   $Opt_Rows          output   (-r) display number of rows in table #
#   $Opt_Tbl_Ctr       output   (-I) display table change counter    #
#   $Opt_Column_Defs   output   (-C) display column definitions      #
#   $Opt_Column_Name   output   (-n) display column name             #
#   $Opt_Column_Type   output   (-t) display column type             #
#   $Opt_Column_Qualifier  output (-q) display column qualifier      #
#   $Opt_Column_DefValue   output (-v) display column default value  #
#   $Opt_No_header     output   (-x) suppress printing header        #
#   $Opt_Column_SDDefn output   (-i) display sd definitions in file  #
#                                    output                          #
#   $Opt_SD_Display    output   (-e) display sd definitions only     #
#   $Opt_Delimiter     output   (-D, -d) delimiter string used in    #
#                                output                              #
#   $Opt_Long_Output   output   (-l) long form ouptut                #
#   $Opt_Print_SY      output   (-s) print system qualified columns  #
#   $Trace             output   (-T) turns verbose mode on           #
#   $Verbose           output   (-V) turns verbose mode on           #
#--------------------------------------------------------------------#
sub parse_cmd_line 
{
# Set up local variables
my(@original_argv) = @ARGV;
my($table_name, $opt_cnt);
my $variable_data = $FALSE;
my $fixed_data = $FALSE;
my $rc = 0;
my @column_names = ();
my %opts = ();

# Process the command line...
if (!getopts('heMINcrCsntqvlidD:xTV', \%opts)) { # options; if errors
    print_usage();                      # display proper usage
    return SR_CLI_BAD_FLAG;             # return nogood
}

# Display help message and quit immediately
if (defined $opts{h}) {                 # -h, help request 
    print_usage();                      # print usage statement
    exit(0);                      
}

if (defined $opts{V}) {                 # -V verbose 
    $Verbose = $TRUE;
}

if (defined $opts{T}) {                 # -T Trace   
    $Trace = $TRUE;
}

# If no operands, that's an error
if ($#ARGV < 0 ) {
    print_usage();
    return SR_CLI_BAD_OPERAND;
}

# Get the operands definitely a table name and optionally a 
# column_name.
$table_name = shift @ARGV; 

# Take in multiple column names if they are given
if (scalar(@ARGV) > 0 ) { @column_names = @ARGV; }

# Check to see if SD definitions are to be displayed first.
# Until such time as it gets more complicated, if it ever
# does, the code will only display the SD definitions, so
# will take a slightly different route to work. By default
# the display is in long format.

if (defined $opts{e}) {
    $Opt_SD_Display = $TRUE;
    while (@ARGV) {
        push @sds_to_display, shift @ARGV;
    }
    return (0, $table_name, 0, 0, \@sds_to_display);

}

# See which options/flags were used...
$opt_cnt = 0;

if (defined $opts{M}) {                  # -M Table Metadata          
    # Equivalent to -INcr
    $Opt_Tbl_Ctr = $TRUE;
    $Opt_Name = $TRUE;
    $Opt_Columns = $TRUE;
    $Opt_Rows = $TRUE;
    $fixed_data = $TRUE;     
    $opt_cnt++;
}

if (defined $opts{I}) {                 # -I get table change counter
    $Opt_Tbl_Ctr = $TRUE;
    $fixed_data = $TRUE;     
    $opt_cnt++;
}

if (defined $opts{N}) {                 # -N get table name
    $Opt_Name = $TRUE; 
    $fixed_data = $TRUE;     
    $opt_cnt++;
}

if (defined $opts{c}) {                 # -c get number of columns
    $Opt_Columns = $TRUE;
    $fixed_data = $TRUE;     
    $opt_cnt++;
}

if (defined $opts{r}) {                 # -r get number of rows
    $Opt_Rows = $TRUE;
    $fixed_data = $TRUE;     
    $opt_cnt++;
}

# Left in for the time that properties become pertinent again
#if (defined $opts{P}) {                 # -P get table Properties
#    $Opt_Tbl_Prop = $TRUE;
#    $fixed_data = $TRUE;     
#    $opt_cnt++;
#}

# -C flag is equivalent to individually specifying flags: ntqv
if (defined $opts{C}) {                     
    $Opt_Column_Name = $TRUE; 
    $Opt_Column_Type = $TRUE; 
    $Opt_Column_Qualifier = $TRUE;
    $Opt_Column_DefValue = $TRUE;
    $variable_data = $TRUE;
    $opt_cnt++;
}

if (defined $opts{n}) {                 # -n get column name(s)
    $Opt_Column_Name= $TRUE; 
    $variable_data = $TRUE;
    $opt_cnt++;
}

if (defined $opts{t}) {                 # -t get column type(s)
    $Opt_Column_Type = $TRUE; 
    $variable_data = $TRUE;
    $opt_cnt++;
}

if (defined $opts{q}) {                 # -q get column qualifier(s)
    $Opt_Column_Qualifier = $TRUE;
    $variable_data = $TRUE;
    $opt_cnt++;
}

if (defined $opts{v}) {                 # -v get column defaults (s)
    $Opt_Column_DefValue = $TRUE;
    $variable_data = $TRUE;
    $opt_cnt++;
}

# Left in for the time that properties become pertinent again
#if (defined $opts{p}) {                 # -p get column Properties
#    $Opt_Column_Prop = $TRUE;
#    $variable_data = $TRUE;     
#    $opt_cnt++;
#}

# If no flags then display entire table definition
if ($opt_cnt == 0) {
    $Opt_Name               = $TRUE;
    $Opt_Columns            = $TRUE;
    $Opt_Rows               = $TRUE; 
    $Opt_Tbl_Ctr            = $TRUE;
#    $Opt_Tbl_Prop           = $TRUE;
    $Opt_Column_Name        = $TRUE;                
    $Opt_Column_Type        = $TRUE;                
    $Opt_Column_Qualifier   = $TRUE;
    $Opt_Column_DefValue    = $TRUE;
#    $Opt_Column_Prop        = $TRUE;
    $variable_data          = $TRUE;     
    $fixed_data             = $TRUE;     
}

# Printing option flags - 

if (defined $opts{x}) {                 # -x exclude headers
    $Opt_No_Header= $TRUE;  
}

if (defined $opts{s}) {                 # -s print system columns     
    $Opt_Print_SY = $TRUE; 
}

# Hierarchy is l > f > d > D > columns (default)
# Enforced in the code. Lesser flags are ignored in the output

if (defined $opts{D}) {                 # -D string delimited output
    $Opt_Delimiter = $opts{D}; 
}

if (defined $opts{d}) {                 # -d string delimited output
    $Opt_Delimiter = ":"; 
}

if (defined $opts{l}) {                 # -l long for output
    $Opt_Long_Output = $TRUE; 
    $Opt_Delimiter = $FALSE;
}

if (defined $opts{i}) {                 # -i input file display
    $Opt_File_Format = $TRUE;           # format output
    $Opt_Column_SDDefn= $TRUE;
    $fixed_data = $FALSE;               # Don't print table metadata
}

return($rc, $table_name, $variable_data, $fixed_data, \@column_names);
}   # end parse_cmd_line


#--------------------------------------------------------------------#
# error_check:                                                       #
#   Checks the return code from the SR function.  If an error is     #
#   detected appropriate error messages will be displayed and        #
#   SR CLI return code set.                                          #
#                                                                    #
# Parameters:                                                        #
#   $sr_function  - Name of the SR function that was called and      #
#                   whose error code we are checking.                #
#   $sr_rc        - SR function return code.                         #
#   $table_name   - Name of the table trying to list.                #
#                                                                    #
# Return values:                                                     #
#   None.                                                            #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub error_check
{
my ($sr_function, $sr_rc, $table_name) = @_;
my $rc = 0;

if ($sr_rc != 0) { 
    if ($sr_rc == SR_NO_PERMISSION) {  
        printEMsg("EMsglssrtbldefPermissionError", $table_name);
        $rc = SR_CLI_USER_ERROR;
    }
    elsif ($sr_rc == SR_NO_TABLE) {
        printCEMsg("EMsgSRcliNoTable", $table_name);
        $rc = SR_CLI_USER_ERROR;    
    }
    elsif ($sr_rc == SR_CONNECTION_LOST) {
        printCEMsg("EMsgSRcliConnectionLost");
        $rc = SR_CLI_REGISTRY_ERROR;
    }
    else {
        printEMsg("EMsglssrtbldefDefinitionError", $table_name);
        printCEMsg("EMsgSRcliSRCommandFailure", $sr_function, $sr_rc);
        $rc = SR_CLI_REGISTRY_ERROR;
    }
}

return $rc;
}   # end error_check


#--------------------------------------------------------------------#
# print_usage : print the usage statement (syntax) to stdout.        #
#   See this command's prologue syntax section for current usage.    #
#--------------------------------------------------------------------#
sub print_usage
{
&printIMsg("IMsglssrtbldefUsage");
}   # end print_usage


#--------------------------------------------------------------------#
# End File                                                           #
#--------------------------------------------------------------------#
